package socketinterface;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;

public class IOHandler implements Handler {
	private final static int BUFFER_SIZE = 4096;
	private List<ByteBuffer> buffers;
	
	public IOHandler() {
		// Initialize the list to save read data
		buffers = new ArrayList<ByteBuffer>();
	}
	
	public void handle(SelectionKey key) throws ClosedChannelException,
			IOException {
		// TODO Auto-generated method stub
		// If available to read, do read
		if (key.isReadable()) {
			read(key);
		}
		
		// If available to write, do write
		if (key.isWritable() && key.isValid()) {
			write(key);
		}
	}
	
	private void read(SelectionKey key) throws ClosedChannelException, IOException {
		SocketChannel channel = (SocketChannel)key.channel();
		
		// Create a buffer for read
		ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
		
		// read from channel
		channel.read(buffer);
		
		// Flip and add a buffer to the list
		buffer.flip();
		buffers.add(buffer);
		
		if (key.interestOps()
			!= (SelectionKey.OP_READ | SelectionKey.OP_WRITE)) {
			// Observe read operation
			key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
		}	
	}

	private void write(SelectionKey key) throws ClosedChannelException, IOException {
		SocketChannel channel = (SocketChannel)key.channel();
		
		if (!buffers.isEmpty()) {
			// If the list is not empty, save the data to take from the head of buffer
			ByteBuffer buffer = buffers.get(0);
			channel.write(buffer);
			
			// Delete buffer when complete to write
			buffers.remove(0);
		} else {
			// Stop observing WRITE_OP if no data to write
			key.interestOps(SelectionKey.OP_READ);
		}
	}
}
